﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Security;
using System.Transactions;

namespace WebChatDAL
{
    public class WebChatDAO : IDisposable
    {
        private WebChatEntities context;

        public WebChatDAO()
        {            
            this.context = new WebChatEntities();
        }
            
        public void  Dispose()
        {
            this.context.Dispose();
        }

        #region Chatters

        public bool TryGetUserByUserId(Guid userId, out aspnet_Users user)
        {
            user = this.context.aspnet_Users.FirstOrDefault(u => u.UserId == userId);
            bool userWasFound = user != null;
            return userWasFound;
        }

        public bool TryGetChatterByUserId(Guid userId, out Chatter chatter)
        {
            chatter = this.context.Chatters.FirstOrDefault(c => c.UserId == userId);
            bool chatterWasFound = chatter != null;
            return chatterWasFound;
        }

        public bool TryGetChatterByChatterId(int chatterId, out Chatter chatter)
        {
            chatter = this.context.Chatters.FirstOrDefault(c => c.ChatterId == chatterId);
            bool chatterWasFound = chatter != null;
            return chatterWasFound;
        }

        public bool TryGetChatterByUserName(string username, out Chatter chatter)
        {
            aspnet_Users user = this.context.aspnet_Users.FirstOrDefault(u => u.UserName == username);

            if (user == null)
            {
                chatter = null;
                return false;
            }

            chatter = this.context.Chatters.FirstOrDefault(c => c.UserId == user.UserId);
            bool chatterWasFound = chatter != null;
            return chatterWasFound;
        }

        public bool TryGetUserByUserName(string username, out aspnet_Users user)
        {
            user = this.context.aspnet_Users.FirstOrDefault(u => u.UserName == username);
            bool userWasFound = user != null;
            return userWasFound;             
        }

        public Chatter CreateChatter(string username, string password, string firstName, string lastName, params string[] roleNames)
        {
            var mUser = Membership.CreateUser(username.Trim(), password);

            using (TransactionScope scope = new TransactionScope())
            {
                try
                {
                    aspnet_Users aUser = this.context.aspnet_Users.FirstOrDefault(u => u.UserName == mUser.UserName);
                    if (aUser == null)
                    {
                        throw new Exception("A user was created but could not get it! call 911 this should never happen :)");
                    }

                    foreach (var roleName in roleNames)
                    {
                        string loweredRoleName = roleName.ToLower();
                        aspnet_Roles roleChatter = this.context.aspnet_Roles.FirstOrDefault(r => r.LoweredRoleName == loweredRoleName);

                        if (roleChatter == null)
                        {
                            throw new Exception("A user was created (not a chatter) but could not find role " + roleName);
                        }

                        aUser.aspnet_Roles.Add(roleChatter);
                    }

                    Chatter chatter = new Chatter();
                    chatter.UserId = aUser.UserId;
                    chatter.FirstName = firstName.Trim();
                    chatter.LastName = lastName.Trim();
                    chatter.RegistrationDate = DateTime.Now;

                    this.context.Chatters.AddObject(chatter);
                    this.context.SaveChanges();

                    scope.Complete();

                    return chatter;
                }
                catch
                {
                    Membership.DeleteUser(username.Trim());
                    throw;
                }
            }        
        }

        public IEnumerable<Chatter> GetChatters()
        {
            return this.context.Chatters.ToList();
        }

        public IEnumerable<ChatterToChatterMessage> GetLastNMessagesByChatterIds(int senderChatterId, int receiverChatterId, int maxMessagesCount = int.MaxValue)
        {
            var messages = (from message in context.ChatterToChatterMessages
                            where (message.SenderChatterId == senderChatterId && message.ReceiverChatterId == receiverChatterId)
                                || (message.SenderChatterId == receiverChatterId && message.ReceiverChatterId == senderChatterId) 
                            orderby message.DateTimeSent descending
                            select message).Take(maxMessagesCount);

            return messages;
        }


        #endregion Chatters

        #region Channels

        public IEnumerable<Channel> GetChannels()
        {
            return this.context.Channels.ToList();
        }

        public bool TryGetChannelByChannelId(int channelId, out Channel channel)
        {
            channel = this.context.Channels.FirstOrDefault(c => c.ChannelId == channelId);
            bool channelWasFound = channel != null;
            return channelWasFound;
        }

        public IEnumerable<ChatterToChannelMessage> GetLastNMessagesByChannelId(int channelId, int maxMessagesCount = int.MaxValue)
        {
            Channel channel;
            if (!this.TryGetChannelByChannelId(channelId, out channel))
            {
                throw new Exception(String.Format("A channel with channelId={0} was not found.", channelId));
            }

            IEnumerable<ChatterToChannelMessage> messages = channel.ChatterToChannelMessages.OrderByDescending(m => m.DateTimeSent).Take(maxMessagesCount);
            return messages;
        }

        public bool TryGetChannelByChannelName(string channelName, out Channel channel)
        {
            channel = this.context.Channels.FirstOrDefault(c => c.ChannelName == channelName);
            bool channelWasFound = channel != null;
            return channelWasFound;
        }

        public Channel CreateChannel(string channelName)
        {
            Channel channel = new Channel();
            channel.ChannelName = channelName.Trim();
            channel.RegistrationDate = DateTime.Now;

            this.context.Channels.AddObject(channel);
            this.context.SaveChanges();

            return channel;
        }

        #endregion Channels

        public void SendMessage_ChatterToChannel(int chatterId, int channelId, string message)
        {
            ChatterToChannelMessage newMessage = new ChatterToChannelMessage();
            newMessage.ChannelId = channelId;
            newMessage.SenderChatterId = chatterId;
            newMessage.MessageContent = message;
            newMessage.DateTimeSent = DateTime.Now;

            this.context.ChatterToChannelMessages.AddObject(newMessage);

            this.context.SaveChanges();
        }

        public void SendMessage_ChatterToChatter(int senderChatterId, int receiverChatterId, string message)
        {
            ChatterToChatterMessage newMessage = new ChatterToChatterMessage();
            newMessage.SenderChatterId = senderChatterId;
            newMessage.ReceiverChatterId = receiverChatterId;
            newMessage.MessageContent = message;
            newMessage.DateTimeSent = DateTime.Now;

            this.context.ChatterToChatterMessages.AddObject(newMessage);

            this.context.SaveChanges();
        }

        public void SaveChanges()
        {
            this.context.SaveChanges();
        }

        public void DeleteChatterByChatterId(int chatterId)
        {
            var chatter = this.context.Chatters.FirstOrDefault(c => c.ChatterId == chatterId);

            if (chatter == null)
            {
                return;
            }

            string username= chatter.aspnet_Users.UserName;

            this.context.Chatters.DeleteObject(chatter);
            this.context.SaveChanges();

            Membership.DeleteUser(username);
        }

        public bool TryDeleteChannelMessageByMessageId(int messageId, string username)
        {
            Chatter chatter;
            if (!this.TryGetChatterByUserName(username, out chatter))
            {
                return false;
            }

            var message = this.context.ChatterToChannelMessages.FirstOrDefault(c => c.MessageId == messageId);

            if (message.SenderChatterId == chatter.ChatterId)
            {
                this.context.ChatterToChannelMessages.DeleteObject(message);
                this.context.SaveChanges();

                return true;
            }

            return false;
        }

        public bool TryGetChannelMessageContentByMessageId(int messageId, out string messageContent)
        {
            var message = this.context.ChatterToChannelMessages.FirstOrDefault(m => m.MessageId == messageId);

            if (message == null)
            {
                messageContent = null;
                return false;
            }

            messageContent = message.MessageContent;
            return true;
        }

        public bool TryGetChatterMessageContentByMessageId(int messageId, out string messageContent)
        {
            var message = this.context.ChatterToChatterMessages.FirstOrDefault(m => m.MessageId == messageId);

            if (message == null)
            {
                messageContent = null;
                return false;
            }

            messageContent = message.MessageContent;
            return true;
        }

        public bool TryDeleteChatterMessageByMessageId(int messageId, string username)
        {
            Chatter chatter;
            if (!this.TryGetChatterByUserName(username, out chatter))
            {
                return false;
            }

            var message = this.context.ChatterToChatterMessages.FirstOrDefault(c => c.MessageId == messageId);

            if (message.SenderChatterId == chatter.ChatterId )
            {
                this.context.ChatterToChatterMessages.DeleteObject(message);
                this.context.SaveChanges();

                return true;
            }

            return false;
        }
    }
}       
